<route lang="json5">
{
  style: {
    navigationBarTitleText: '设备配网',
  },
}
</route>

<template>
  <view class="device-config">
    <view class="guide">
      <view class="guide-title">设备配网</view>
      <view class="guide-desc">请选择要配置给设备的WiFi网络</view>
    </view>

    <!-- WiFi列表 -->
    <view class="wifi-list">
      <view
        v-for="item in wifiList"
        :key="item.SSID"
        class="wifi-item"
        :class="{ 'wifi-item-selected': selectedWifi?.SSID === item.SSID }"
        @tap="selectWifi(item)"
      >
        <view class="wifi-info">
          <text class="wifi-name">{{ item.SSID }}</text>
          <text class="wifi-signal">信号强度: {{ item.signalStrength }}%</text>
        </view>
        <text class="wifi-secure" v-if="item.secure">已加密</text>
      </view>
    </view>

    <!-- 配网弹窗 -->
    <view class="modal" v-if="showConfigModal">
      <view class="modal-content">
        <view class="modal-title">WiFi配网</view>
        <view class="modal-body">
          <view class="form-item">
            <text class="form-label">WiFi名称</text>
            <text class="form-value">{{ selectedWifi?.SSID }}</text>
          </view>
          <view class="form-item">
            <text class="form-label">WiFi密码</text>
            <input
              class="password-input"
              v-model="wifiPassword"
              type="text"
              password
              placeholder="请输入WiFi密码"
            />
          </view>
        </view>
        <view class="modal-footer">
          <button class="btn btn-cancel" @tap="closeConfigModal">取消</button>
          <button
            class="btn btn-confirm"
            :disabled="!wifiPassword"
            :loading="isConfiguring"
            @tap="startConfig"
          >
            {{ isConfiguring ? '配网中...' : '开始配网' }}
          </button>
        </view>
      </view>
    </view>

    <!-- 加载提示 -->
    <view class="loading" v-if="isLoading">
      <text>搜索WiFi中...</text>
    </view>

    <!-- 悬浮日志按钮和面板 -->
    <view class="log-wrapper">
      <!-- 日志展开按钮 -->
      <view
        class="log-fab"
        :class="{ 'log-fab-active': showLogPanel }"
        :style="{ bottom: showLogPanel ? '320px' : '20px' }"
        @tap="toggleLogPanel"
      >
        <text class="log-fab-text">{{ showLogPanel ? '收起' : '日志' }}</text>
        <text class="log-fab-badge" v-if="hasError">!</text>
      </view>

      <!-- 日志面板 -->
      <view class="log-container" :class="{ 'log-container-show': showLogPanel }">
        <view class="log-header">
          <text class="log-title">配网日志</text>
          <text class="clear-log" @tap="clearLogs">清除日志</text>
        </view>
        <scroll-view
          class="log-content"
          scroll-y
          :scroll-top="scrollTop"
          @scrolltoupper="onScrollToUpper"
          @scrolltolower="onScrollToLower"
        >
          <view
            v-for="(log, index) in logs"
            :key="index"
            class="log-item"
            :class="{ 'log-error': log.type === 'error' }"
          >
            <text class="log-time">{{ log.time }}</text>
            <text class="log-msg">{{ log.message }}</text>
          </view>
        </scroll-view>
      </view>
    </view>
  </view>
</template>

<script lang="ts" setup>
import { ref, nextTick, onUnmounted } from 'vue'

// WiFi列表数据
const wifiList = ref<WechatMiniprogram.GetWifiListSuccessCallbackResult[]>([])
const selectedWifi = ref<WechatMiniprogram.WiFi>()
const wifiPassword = ref('')
const isLoading = ref(false)
const isConfiguring = ref(false)
const showConfigModal = ref(false)

// 日志相关
interface LogItem {
  time: string
  message: string
  type: 'info' | 'error'
}

const logs = ref<LogItem[]>([])
const scrollTop = ref(0)
const autoScroll = ref(true)
const showLogPanel = ref(false)
const hasError = ref(false)

// 切换日志面板显示状态
const toggleLogPanel = () => {
  showLogPanel.value = !showLogPanel.value
}

// 修改添加日志函数，增加错误标记
const addLog = (message: string, type: 'info' | 'error' = 'info') => {
  const time = new Date().toLocaleTimeString()
  logs.value.push({ time, message, type })

  // 标记是否有错误
  if (type === 'error') {
    hasError.value = true
  }

  // 如果开启了自动滚动，则滚动到底部
  if (autoScroll.value && showLogPanel.value) {
    nextTick(() => {
      scrollTop.value = 99999
    })
  }
}

// 修改清除日志函数，同时清除错误标记
const clearLogs = () => {
  logs.value = []
  hasError.value = false
}

// 滚动事件处理
const onScrollToUpper = () => {
  console.log('滚动到顶部')
}

const onScrollToLower = () => {
  autoScroll.value = true
}

// 获取WiFi列表
const getWifiList = async () => {
  isLoading.value = true
  addLog('开始获取WiFi列表...')
  try {
    await wx.getWifiList()
    addLog('获取WiFi列表请求成功')
  } catch (err: any) {
    const errMsg = `获取WiFi列表失败: ${err.errMsg || err.message || '未知错误'}`
    addLog(errMsg, 'error')
    console.error(errMsg, err)
    uni.showToast({
      title: '获取WiFi列表失败',
      icon: 'none',
    })
  }
}

// 监听WiFi列表
wx.onGetWifiList((res) => {
  isLoading.value = false
  wifiList.value = res.wifiList.sort((a, b) => b.signalStrength - a.signalStrength)
  addLog(`成功获取到 ${res.wifiList.length} 个WiFi`)
})

// 选择WiFi时显示配网弹窗
const selectWifi = (wifi: WechatMiniprogram.WiFi) => {
  selectedWifi.value = wifi
  wifiPassword.value = ''
  showConfigModal.value = true
  addLog(`选择WiFi: ${wifi.SSID}`)
}

// 关闭配网弹窗
const closeConfigModal = () => {
  showConfigModal.value = false
  wifiPassword.value = ''
}

// UDP Socket 实例
let udpSocket: WechatMiniprogram.UDPSocket | null = null

// 开始配网
const startConfig = async () => {
  if (!selectedWifi.value || !wifiPassword.value) return

  isConfiguring.value = true
  addLog('开始配网...', 'info')
  addLog(`目标WiFi: ${selectedWifi.value.SSID}`, 'info')

  try {
    // 1. 创建 UDP Socket
    addLog('创建UDP Socket...', 'info')
    udpSocket = wx.createUDPSocket()

    // 2. 监听事件
    udpSocket.onListening(() => {
      addLog('UDP Socket开始监听', 'info')
    })

    udpSocket.onMessage((res) => {
      const message = arrayBufferToString(res.message)
      addLog('收到设备消息 <<<<<<', 'info')
      addLog(`来自: ${res.remoteInfo.address}:${res.remoteInfo.port}`, 'info')
      addLog(`数据: ${message}`, 'info')

      try {
        const result = JSON.parse(message)
        if (result.success) {
          addLog('配网成功!', 'info')
          uni.showToast({
            title: '配网成功',
            icon: 'success',
          })
          closeUDP()
          setTimeout(() => {
            uni.navigateBack()
          }, 1500)
        } else {
          const errMsg = `配网失败: ${result.message || '未知错误'}`
          addLog(errMsg, 'error')
          uni.showToast({
            title: '配网失败',
            icon: 'error',
          })
        }
      } catch (err: any) {
        addLog(`解析设备响应失败: ${err.message}`, 'error')
      }
      isConfiguring.value = false
    })

    udpSocket.onError((err) => {
      const errMsg = `UDP错误: ${err.errMsg || '未知错误'}`
      addLog(errMsg, 'error')
      console.error('UDP错误:', err)
    })

    udpSocket.onClose(() => {
      addLog('UDP连接已关闭', 'info')
    })

    // 3. 绑定端口
    const port = udpSocket.bind()
    addLog(`UDP绑定本地端口: ${port}`, 'info')

    // 4. 准备发送数据
    const configData = {
      ssid: selectedWifi.value.SSID,
      password: wifiPassword.value,
    }

    // 5. 转换为 ArrayBuffer
    const buffer = stringToArrayBuffer(JSON.stringify(configData))

    // 6. 发送数据
    addLog('发送配网数据 >>>>>>', 'info')
    addLog(`目标地址: 192.168.4.1:4210`, 'info')
    addLog(`发送配网数据: ${JSON.stringify(configData, null, 2)}`, 'info')

    udpSocket.send({
      address: '192.168.4.1',
      port: 4210,
      message: buffer,
    })

    addLog('数据发送完成', 'info')

    // 7. 设置超时处理
    setTimeout(() => {
      if (isConfiguring.value) {
        isConfiguring.value = false
        const timeoutMsg = '配网超时，30秒内未收到设备响应'
        addLog(timeoutMsg, 'error')
        uni.showToast({
          title: '配网超时',
          icon: 'none',
        })
        closeUDP()
      }
    }, 30000)
  } catch (err: any) {
    isConfiguring.value = false
    const errMsg = `配网失败: ${err.errMsg || err.message || '未知错误'}`
    addLog(errMsg, 'error')
    uni.showToast({
      title: '配网失败',
      icon: 'none',
    })
    console.error('配网失败:', err)
    closeUDP()
  }
}

// 关闭 UDP 时也记录日志
const closeUDP = () => {
  if (udpSocket) {
    try {
      udpSocket.close()
      addLog('主动关闭UDP连接', 'info')
      udpSocket = null
    } catch (err: any) {
      addLog(`关闭UDP失败: ${err.message}`, 'error')
      console.error('关闭UDP失败:', err)
    }
  }
}

// ArrayBuffer 转 String
const arrayBufferToString = (buffer: ArrayBuffer): string => {
  return String.fromCharCode.apply(null, new Uint8Array(buffer))
}

// String 转 ArrayBuffer
const stringToArrayBuffer = (str: string): ArrayBuffer => {
  const buffer = new ArrayBuffer(str.length)
  const view = new Uint8Array(buffer)
  for (let i = 0; i < str.length; i++) {
    view[i] = str.charCodeAt(i)
  }
  return buffer
}

// 组件卸载时清理
onUnmounted(() => {
  closeUDP()
})

// 页面加载时获取WiFi列表
onLoad(() => {
  getWifiList()
})
</script>

<style lang="scss" scoped>
.device-config {
  min-height: 100vh;
  padding: 20px;
  background: #f5f5f5;
}

.guide {
  margin-bottom: 30px;
  text-align: center;

  &-title {
    margin-bottom: 10px;
    font-size: 24px;
    font-weight: bold;
    color: #333;
  }

  &-desc {
    font-size: 14px;
    color: #666;
  }
}

.wifi-list {
  padding: 10px;
  margin-bottom: 20px;
  background: #fff;
  border-radius: 12px;
}

.wifi-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 15px;
  border-bottom: 1px solid #eee;

  &:last-child {
    border-bottom: none;
  }

  &-selected {
    background: #e6f7ff;
  }
}

.wifi-info {
  flex: 1;
}

.wifi-name {
  margin-bottom: 4px;
  font-size: 16px;
  color: #333;
}

.wifi-signal {
  font-size: 12px;
  color: #999;
}

.wifi-secure {
  font-size: 12px;
  color: #52c41a;
}

.wifi-form {
  padding: 20px;
  margin-bottom: 20px;
  background: #fff;
  border-radius: 12px;

  .form-title {
    margin-bottom: 15px;
    font-size: 16px;
    color: #333;
  }
}

.password-input {
  width: 100%;
  height: 44px;
  padding: 0 15px;
  margin-bottom: 20px;
  font-size: 14px;
  border: 1px solid #ddd;
  border-radius: 6px;
}

.config-btn {
  width: 100%;
  height: 44px;
  font-size: 16px;
  color: #fff;
  background: #1890ff;
  border-radius: 6px;

  &[disabled] {
    background: #ccc;
  }
}

.loading {
  position: fixed;
  top: 50%;
  left: 50%;
  padding: 10px 20px;
  font-size: 14px;
  color: #fff;
  background: rgba(0, 0, 0, 0.7);
  border-radius: 4px;
  transform: translate(-50%, -50%);
}

// 日志包装器
.log-wrapper {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 100;
}

// 悬浮按钮样式
.log-fab {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 50px;
  height: 50px;
  background: #1890ff;
  border-radius: 50%;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  transition: all 0.3s ease;

  &-text {
    font-size: 14px;
    color: #fff;
  }

  &-active {
    background: #096dd9;
  }

  &-badge {
    position: absolute;
    top: -2px;
    right: -2px;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    font-size: 12px;
    color: #fff;
    background: #ff4d4f;
    border-radius: 50%;
  }
}

// 日志容器样式更新
.log-container {
  position: fixed;
  right: 0;
  bottom: -100%;
  left: 0;
  height: 300px;
  padding: 10px;
  background: #1a1a1a;
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  transition: all 0.3s ease;

  &-show {
    bottom: 0;
  }
}

.log-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 30px;
  padding: 0 10px;
  border-bottom: 1px solid #333;
}

.log-title {
  font-size: 14px;
  font-weight: bold;
  color: #fff;
}

.clear-log {
  font-size: 12px;
  color: #666;
}

.log-content {
  height: calc(100% - 30px);
  padding: 5px 0;
}

.log-item {
  padding: 3px 10px;
  font-size: 12px;
  color: #fff;

  &.log-error {
    color: #ff4d4f;
  }
}

.log-time {
  margin-right: 8px;
  color: #666;
}

.log-msg {
  word-break: break-all;
}

// 刷新按钮
.refresh-btn {
  padding: 10px;
  margin-bottom: 20px;
  font-size: 14px;
  color: #1890ff;
  text-align: center;
}

// 重试按钮
.retry-btn {
  padding: 10px;
  margin-top: 20px;
  font-size: 14px;
  color: #1890ff;
  text-align: center;
}

// 弹窗样式
.modal {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.6);
}

.modal-content {
  width: 80%;
  overflow: hidden;
  background: #fff;
  border-radius: 12px;
}

.modal-title {
  padding: 15px;
  font-size: 18px;
  color: #333;
  text-align: center;
  border-bottom: 1px solid #eee;
}

.modal-body {
  padding: 20px;
}

.form-item {
  margin-bottom: 15px;

  &:last-child {
    margin-bottom: 0;
  }
}

.form-label {
  display: block;
  margin-bottom: 8px;
  font-size: 14px;
  color: #666;
}

.form-value {
  font-size: 16px;
  color: #333;
}

.modal-footer {
  display: flex;
  border-top: 1px solid #eee;
}

.btn {
  flex: 1;
  height: 50px;
  font-size: 16px;
  line-height: 50px;
  text-align: center;
  background: none;
  border: none;

  &-cancel {
    color: #666;
    border-right: 1px solid #eee;
  }

  &-confirm {
    color: #1890ff;

    &[disabled] {
      color: #ccc;
    }
  }
}

// 更新日志按钮样式
.log-fab {
  position: fixed;
  right: 20px;
  z-index: 101;
  transition: all 0.3s ease;
}

// 更新日志容器样式
.log-container {
  position: fixed;
  right: 0;
  bottom: -100%;
  left: 0;
  z-index: 100;
  height: 300px;
  padding: 10px;
  background: #1a1a1a;
  border-top-left-radius: 16px;
  border-top-right-radius: 16px;
  transition: all 0.3s ease;

  &-show {
    bottom: 0;
  }
}
</style>
